package com.boardour.socket.server;

import android.util.Log;

import com.boardour.socket.foo.constants.TCPConstants;
import com.boardour.socket.foo.handle.ConnectorCloseChain;
import com.boardour.socket.foo.handle.ConnectorHandler;
import com.boardour.socket.foo.handle.ConnectorStringPacketChain;
import com.boardour.socket.foo.handle.OnReceivedPacketListener;

import net.qiujuer.library.clink.box.StringReceivePacket;
import net.qiujuer.library.clink.core.Connector;
import net.qiujuer.library.clink.core.ReceivePacket;
import net.qiujuer.library.clink.core.ScheduleJob;
import net.qiujuer.library.clink.core.schedule.IdleTimeoutScheduleJob;
import net.qiujuer.library.clink.utils.CloseUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 服务端
 */
public class TCPServer implements ServerAcceptor.AcceptListener, OnReceivedPacketListener {
    public static final String TAG = "TCPServer";
    private final int port;
    private final List<ConnectorHandler> connectorHandlerList = new ArrayList<>();
    private ServerAcceptor acceptor;
    private ServerSocketChannel server;
    private final ServerStatistics statistics = new ServerStatistics();
    private OnReceivedPacketListener onReceivedPacketListener;


    public TCPServer(int port) {
        this.port = port;
    }

    public boolean start() {
        try {
            // 启动Acceptor线程
            ServerAcceptor acceptor = new ServerAcceptor(this);
            ServerSocketChannel server = ServerSocketChannel.open();
            // 设置为非阻塞
            server.configureBlocking(false);
            // 绑定本地端口
            ServerSocket socket = server.socket();
            socket.setReceiveBufferSize(128 * 1024 * 1024);
            socket.setPerformancePreferences(1, 7, 2);
            socket.setReuseAddress(true);
            socket.bind(new InetSocketAddress(port));
            // 注册客户端连接到达监听
            server.register(acceptor.getSelector(), SelectionKey.OP_ACCEPT);

            this.server = server;
            this.acceptor = acceptor;

            // 线程需要启动
            acceptor.start();
            if (acceptor.awaitRunning()) {
                Log.e(TAG, "服务器准备就绪～");
                Log.e(TAG, "服务器信息：" + server.toString());
                return true;
            } else {
                Log.e(TAG, "启动异常！");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            stop();
            return false;
        }
    }

    /**
     * 新客户端链接时回调
     *
     * @param channel 新客户端
     */
    @Override
    public void onNewSocketArrived(SocketChannel channel) {
        try {
            ConnectorHandler connectorHandler = new ConnectorHandler(channel, TCPServer.this);
            Log.e(TAG, connectorHandler.getClientInfo() + ":Connected!");

            // 添加收到消息的处理责任链
            connectorHandler.getStringPacketChain()
                    .appendLast(statistics.statisticsChain())
                    .appendLast(new ParseAudioStreamCommandStringPacketChain());

            // 添加关闭链接时的责任链
            connectorHandler.getCloseChain()
                    .appendLast(new RemoveAudioQueueOnConnectorClosedChain())
                    .appendLast(new RemoveQueueOnConnectorClosedChain());

            // 空闲任务调度
            ScheduleJob scheduleJob = new IdleTimeoutScheduleJob(20, TimeUnit.SECONDS, connectorHandler);
            connectorHandler.schedule(scheduleJob);

            synchronized (connectorHandlerList) {
                connectorHandlerList.add(connectorHandler);
                Log.e(TAG, "当前客户端数量：" + connectorHandlerList.size());
            }

            // 回送客户端在服务器的唯一标志
            sendMessageToClient(connectorHandler, TCPConstants.COMMAND_INFO_NAME + connectorHandler.getKey().toString());
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "客户端链接异常：" + e.getMessage());
        }
    }

    /**
     * 关闭操作
     */
    public void stop() {
        if (acceptor != null) {
            acceptor.exit();
        }

        ConnectorHandler[] connectorHandlers;
        synchronized (connectorHandlerList) {
            connectorHandlers = connectorHandlerList.toArray(new ConnectorHandler[0]);
            connectorHandlerList.clear();
        }
        for (ConnectorHandler connectorHandler : connectorHandlers) {
            connectorHandler.exit();
        }

        CloseUtils.close(server);
    }

    /**
     * 发送消息给某个客户端
     *
     * @param handler 客户端
     * @param msg     消息
     */
    public void sendMessageToClient(ConnectorHandler handler, String msg) {
        handler.send(msg);
        statistics.sendSize++;
    }


    public void setOnReceiveListener(OnReceivedPacketListener onReceiveListener) {
        this.onReceivedPacketListener = onReceiveListener;
    }

    @Override
    public void onReceivedPacket(ReceivePacket packet, SocketChannel channel) {
        //Log.e(TAG, "onReceivedPacket");
        // Log.e(TAG, "接收到客户端的Packet：" + packet.type());
        if (onReceivedPacketListener == null) {
            return;
        }
        onReceivedPacketListener.onReceivedPacket(packet, channel);
    }


    /**
     * 在链接关闭回调时
     */
    private class RemoveQueueOnConnectorClosedChain extends ConnectorCloseChain {

        @Override
        protected boolean consume(ConnectorHandler handler, Connector connector) {
            synchronized (connectorHandlerList) {
                connectorHandlerList.remove(handler);
            }
            return true;
        }
    }


    /**
     * 从全部列表中通过Key查询到一个链接
     */
    private ConnectorHandler findConnectorFromKey(String key) {
        synchronized (connectorHandlerList) {
            for (ConnectorHandler connectorHandler : connectorHandlerList) {
                if (connectorHandler.getKey().toString().equalsIgnoreCase(key)) {
                    return connectorHandler;
                }
            }
        }
        return null;
    }

    // 音频命令控制与数据流传输链接映射表
    private final HashMap<ConnectorHandler, ConnectorHandler> audioCmdToStreamMap = new HashMap<>(10);
    private final HashMap<ConnectorHandler, ConnectorHandler> audioStreamToCmdMap = new HashMap<>(10);


    /**
     * 音频命令解析
     */
    private class ParseAudioStreamCommandStringPacketChain extends ConnectorStringPacketChain {
        @Override
        protected boolean consume(ConnectorHandler handler, StringReceivePacket stringReceivePacket) {
            String str = stringReceivePacket.entity();
            if (str.startsWith(TCPConstants.COMMAND_CONNECTOR_BIND)) {
                // 绑定命令，也就是将音频流绑定到当前的命令流上
                String key = str.substring(TCPConstants.COMMAND_CONNECTOR_BIND.length());
                ConnectorHandler audioStreamConnector = findConnectorFromKey(key);
                if (audioStreamConnector != null) {
                    // 添加绑定关系
                    audioCmdToStreamMap.put(handler, audioStreamConnector);
                    audioStreamToCmdMap.put(audioStreamConnector, handler);
                }
            } else {
                return false;
            }
            return true;
        }
    }

    /**
     * 链接关闭时操作
     */
    private class RemoveAudioQueueOnConnectorClosedChain extends ConnectorCloseChain {

        @Override
        protected boolean consume(ConnectorHandler handler, Connector connector) {
            if (audioCmdToStreamMap.containsKey(handler)) {
                // 命令链接断开
                audioCmdToStreamMap.remove(handler);
            } else if (audioStreamToCmdMap.containsKey(handler)) {
                // 流断开
                audioStreamToCmdMap.remove(handler);
                // 解散房间
            }
            return false;
        }
    }

    public ConnectorHandler getConnector() {
        if (connectorHandlerList != null && connectorHandlerList.size() > 0) {
            return connectorHandlerList.get(connectorHandlerList.size() - 1);
        }
        return null;
    }

}
